#pragma once
#include <unordered_map>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"

#define DEFAULTPORT 8000
#define DEFAULTIP "0.0.0.0"

using std::function;
using std::unordered_map;

using func_t = function<string(const string &)>;

enum
{
    socketerror = 1,
    binderror = 2
};

log ls;

class Udpserve
{
public:
    Udpserve(const uint16_t &port = DEFAULTPORT, const string &ip = DEFAULTIP)
        : _sockfd(0), _port(port), _ip(ip), _running(false)
    {
    }

    void init()
    {
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            ls(Fatal, "socker error, _sockfd: %d", _sockfd);
            exit(socketerror);
        }
        ls(Info, "socker success, _sockfd: %d", _sockfd);
        // bind
        sockaddr_in serve;
        bzero(&serve, sizeof(serve));
        serve.sin_family = AF_INET;
        serve.sin_addr.s_addr = inet_addr(_ip.c_str());
        serve.sin_port = htons(_port);

        if (bind(_sockfd, (sockaddr *)&serve, sizeof(serve)) < 0)
        {
            ls(Fatal, "bind error, errno: %d, err string: %s", errno, strerror(errno));
            exit(binderror);
        }
        // else
        ls(Info, "bind success, _sockfd: %d", _sockfd);
    }

    void checkUser(const sockaddr_in& client){
        string ip = inet_ntoa(client.sin_addr);
        auto it = _user.find(ip);
        if(it == _user.end()){
            _user.insert({ip, client});
            std::cout << "[" << ip << ":" << ntohs(client.sin_port) << "] add to online user." << std::endl;
        }
    }

    void dispose(const sockaddr_in& client, string& s){
        for(auto& it : _user){
            string message = "[";
            message += inet_ntoa(client.sin_addr);
            message += " : ";
            message += ntohs(client.sin_port);
            message += "]: ";
            message += s;
            sendto(_sockfd, message.c_str(), message.size(), 0, (sockaddr *)&(it.second), sizeof(it.second));
        }
    }

    void run(func_t func)
    {
        _running = true;
        while (_running)
        {
            sockaddr_in client;
            bzero(&client, sizeof(client));

            char buf[2024] = {0};
            socklen_t len = sizeof(client);

            // ls(Info, "recvfrom befor,ip: %s, port: %d", _ip.c_str(), _port);

            ssize_t n = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, (sockaddr *)&client, &len);
            if (n < 0)
            {
                ls(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));
                sleep(1);
                continue;
            }
            // else{
            //     ls(Info, "recvfrom after");
            // }
            // ls(Info, "recvfrom success, recvfrom bytes: %d", n);

            checkUser(client);

            buf[n] = 0;
            string echo_string = func(buf);
            dispose(client, echo_string);
        }
    }

    ~Udpserve()
    {
        if (_sockfd > 0)
            close(_sockfd);
    }

private:
    int _sockfd;
    uint16_t _port;
    string _ip;
    bool _running;
    unordered_map<string, sockaddr_in> _user;
};